package explorandum.g6;

import java.awt.Color;
import java.awt.Point;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Random;

import explorandum.Logger;
import explorandum.Move;
import explorandum.Player;

public class G6Player implements Player {
	public final int MAX_BEHAVIOR_TRANSITIONS = 3;
	
	Logger log;
	public Random rand;
	public static Map map;

	private WeavingBehavior weavingBehavior;
	private CoastFollowingBehavior coastFollowingBehavior;
	private GoToUncoveredAreaBehavior goToUncoveredAreaBehavior;
	private HillClimbingBehavior hillClimbingBehavior;
	public Behavior currentBehavior;
	protected static ArrayList<Point> history; //to store the history of points been on
	protected static HashSet<Point> pointsOnBridges; //to store the points that are known to be on a bridge
	private double totalTime = 0;
	private double averageTime = 0;
	private int numberOfMoves = 0;
	private double maxTime = -100000;

	int range;
	int explorers;
	
	public void register(int explorerID, int rounds, int explorers, int range,
			Logger log, Random rand) {
		
		this.range = range;
		this.explorers = explorers;
		
		this.log = log;
		this.rand = rand;
		history = new ArrayList<Point>();
		pointsOnBridges = new HashSet<Point>();
		map = new Map();
		weavingBehavior = new WeavingBehavior(log, range, map);
		goToUncoveredAreaBehavior = new GoToUncoveredAreaBehavior(log, map, weavingBehavior);
		coastFollowingBehavior = new CoastFollowingBehavior(map, log, range, explorers, 250);
		hillClimbingBehavior = new HillClimbingBehavior(log, map);
//		currentBehavior = coastFollowingBehavior;
		currentBehavior = goToUncoveredAreaBehavior;
//		log.debug("\nRounds:" + rounds + "\nExplorers:" + explorers
//				+ "\nRange:" + range);
	}

	public Color color() throws Exception {
		return Color.CYAN;
	}
	
	
	public String name() {
		return "Captain Kirk";
	}

	/**
	 * positions are relative to starting position.
	 */
	public Move move(Point currentLocation, Point[] offsets,
			Boolean[] hasExplorer, Integer[][] otherExplorers,
			Integer[] terrain, int time) throws Exception {
		
		//New thread
		//Timer timer = new Timer(map, log, rand, range, explorers, currentLocation, offsets, hasExplorer, otherExplorers, terrain, time);
		
		Move m;
		long time1 = System.currentTimeMillis();
		
		if(time < 50)
			Util.dontGoOnBridges = false;
		else if(time == 50)
			Util.dontGoOnBridges = true;
		
//		//start thread
//		timer.start();
//		try {
//			timer.join(900);
//			
//			
//			if(timer.isAlive()) //if move is still being calculated after 990ms
//			{
//				//System.out.println("Almost struck out! Moving randomly!");
//				timer.interrupt();
//				return new Move(GameConstants.ACTIONS[rand.nextInt(GameConstants.ACTIONS.length-1)+1]);
//			}
//			m = timer.getMove();
//			
		try{
			m = realMove(currentLocation, offsets, hasExplorer, otherExplorers, terrain, time);
			long time2 = System.currentTimeMillis();
			totalTime += time2-time1;
			if((time2-time1) > maxTime)
				maxTime = time2-time1;
			++numberOfMoves;
			averageTime = totalTime/numberOfMoves;
			
			//System.out.println("Average time per move(ms): " + averageTime  + ", Max time(ms): " + maxTime);
			
			return m;
		} catch (Throwable error) {
			error.printStackTrace();
			return new Move(ACTIONS[rand.nextInt(ACTIONS.length)]);
		}
	}
	
	public Move realMove(Point currentLocation, Point[] offsets,
			Boolean[] hasExplorer, Integer[][] otherExplorers,
			Integer[] terrain, int time) throws Exception {
		
		
		updateMap(currentLocation, offsets, hasExplorer, terrain);

		Move move = currentBehavior.move(currentLocation, offsets, hasExplorer,
				otherExplorers, terrain, time);
		
		/*way to fool eclipse into returning before end of method
		   int t=5;
		if(t==5)
			return move;
		*/
		
		// If current behavior doesn't have a move for us, pick a new behavior.
		for (int i = 0; move == null; i++) {
			// Make sure we don't infinitely loop
			if (i >= MAX_BEHAVIOR_TRANSITIONS) {
				int action = ACTIONS[rand.nextInt(ACTIONS.length)];
				//log.debug("Move " + ACTION_NAMES[action] + " acquired from random behavior.");
				return new Move(action);
			}
			currentBehavior = getNextBehavior(currentBehavior);
			move = currentBehavior.move(currentLocation, offsets, hasExplorer,
					otherExplorers, terrain, time);
		}
		
		// Debug output
		if (currentBehavior == goToUncoveredAreaBehavior) {
			map.setRegion(goToUncoveredAreaBehavior.getCurrentRegion());
		} else {
			map.setRegion(null);
		}
		//log.debug("map:\n" + map.toString(currentLocation));
		//log.debug("Move " + ACTION_NAMES[move.getAction()] + " acquired from " + currentBehavior.getName() + " behavior.");
		
		return move;
	}

	public void updateMap(Point currentLocation, Point[] offsets,
			Boolean[] hasExplorer, Integer[] terrain) {
		for (int i = 0; i < offsets.length; i++) {
			Cell cell = new Cell(terrain[i], hasExplorer[i]);
			map.set(offsets[i].x, offsets[i].y, cell);
		}
		history.add(currentLocation);
	}

	/**
	 * Transition "table" for the state machine which determines behavior.
	 */
	public Behavior getNextBehavior(Behavior currentBehavior) {
		if (currentBehavior == coastFollowingBehavior) {
			return goToUncoveredAreaBehavior;
		} else if (currentBehavior == hillClimbingBehavior) {
			return goToUncoveredAreaBehavior;
		} else if (currentBehavior == goToUncoveredAreaBehavior) {
			return weavingBehavior;
		} else if(currentBehavior == weavingBehavior) {
			return goToUncoveredAreaBehavior;
		} else {
			assert false : "Invalid input to getNextBehavior";
			return weavingBehavior;
		}
	}
	
	public List<Point> getPointsToHighlight() {
		List<Point> points = null;
		if (currentBehavior == goToUncoveredAreaBehavior) {
			points = goToUncoveredAreaBehavior.getCurrentRegion();
		}
		if (points == null) {
			points = new ArrayList<Point>();
		}
		return points;
	}
}
